#!/usr/bin/python3
# -*- coding: utf-8 -*-
# coding=utf-8
# Author: BillySir
from __future__ import print_function, annotations
from typing import *
from chess import *


class Cell:
    """棋盘上的格(一格可放一棋子)"""

    def __init__(self, basket: ChessBasket):
        super(Cell, self).__init__()
        self._chess: Chess = None   # 棋子
        self._board: ChessBoard = None  # 棋盘
        self.index: int = None      # cell index
        self.children: List[Cell] = []  # child cells
        self.parents: List[Cell] = []   # parent cells
        self.next_sibling: Cell = None  # 下一个计算的cell
        self._basket = basket   # 棋盒

    def bind_parents(self, parents: List[Cell]) -> None:
        """绑定parent"""
        for parent in parents:
            parent.children.append(self)
            self.parents.append(parent)

    @property
    def board(self):
        return self._board

    @board.setter
    def board(self, value):
        if self._board is not None:
            raise Exception("can not set board property when it is not none")
        self._board = value

    @property
    def chess(self) -> Chess:
        return self._chess

    def set_chess(self, chess: Chess) -> bool:
        """落子，并返回是否成功"""
        if self.chess == chess:
            return True
        self._set_chess(chess)
        if not self.validate():
            self.reset()
            return False
        return self.validate_by_children(self)

    def _set_chess(self, chess: Chess) -> None:
        from rule import Rule
        if self._chess is not None and not isinstance(self, Rule):
            raise Exception(
                "Can not set chess when chess is not None. index:" + str(self.index) + ",chess:" + str(self._chess))
        if chess is None:
            raise Exception("Can not set to be None. please use reset(). index:" + str(self.index))
        if self._board is not None:
            self._basket.use_chess(chess)
        self._chess = chess

    def validate_by_children(self, caller) -> bool:
        """当且仅当我有变化，引发我孩子的验证"""
        if self._chess is None:
            return True
        for child in self.children:
            if not child.validate():
                self.reset()
                return False
        return True

    def validate(self) -> bool:
        """验证。验证parent cells或验证self cell"""
        return True

    def reset(self) -> None:
        """重置，使自己的chess为None"""
        if self._chess is None:  # 默认是遇到None就结束，若要特殊请override
            return
        self._basket.unuse_chess(self._chess)
        self._chess = None  # 默认重置是简单地设为None，若要特殊请override
        if self.next_sibling is None:
            return
        self.next_sibling.reset()

    def guess(self) -> bool:
        """尝试落棋子"""
        chess_list = self.get_chess_list_to_fill()

        for chess in chess_list:
            if self._board.print_process:
                print("%s:%s%d guess %s" % (str(self._board), "." * self.index, self.index, chess))
            if not self.set_chess(chess):
                self.reset()
                continue
            if self._to_next_sibling():
                return True
        return False

    def get_chess_list_to_fill(self):
        """得到chessBasket和全部child共同计算的可下子交集"""
        chess_list = self._basket.get_chess_list_to_fill[:]
        for child in self.children:
            chess_list = child.can_fill(chess_list, self)
        return chess_list

    def _to_next_sibling(self):
        if self.next_sibling is None:
            self._board.print_answer()
            # return True  # 只求一个解时return True，否则不要return
        elif self.next_sibling.guess():
            return True
        self.reset()
        return False

    def can_fill(self, chess_list, parent: Cell):
        """获取可下的棋子集合与给定的chess_list的交集"""
        return chess_list

    def __str__(self):
        return "cell[%d]:%s" % (self.index, self._chess)

    __repr__ = __str__


class ChessBoard:
    """棋盘"""

    def __init__(self, cells: List[Cell]):
        super(ChessBoard, self).__init__()
        if len(cells) == 0:
            raise Exception("cells can not be empty")

        for i in range(len(cells)):
            cells[i].board = self
            cells[i].index = i
        ChessBoard.build_sibling_chain(cells)
        self._cells = cells
        self._head_cell: Cell = cells[0]    # 头cell，代表第一个计算的cell，默认是cells[0]

        self._rules = []        # 规范集

        self._answer_count = 0  # 解数量
        self.print_process: bool = False    # 是否打印解题(尝试)过程
        self.width = 0          # 棋盘宽度
        self.height = 0         # 棋盘高度

    @staticmethod
    def build_sibling_chain(cells: List[Cell]):
        """建立代表计算顺序的sibling链"""
        for i in range(len(cells) - 1):
            cells[i].next_sibling = cells[i + 1]

    @property
    def cells(self):
        return self._cells

    @property
    def rules(self):
        return self._rules

    @rules.setter
    def rules(self, rules: List[Cell]):
        for i in range(len(rules)):
            rules[i].board = self
            rules[i].index = i
        self._rules = rules

    def put_chess(self, index: int, chess: Chess) -> None:
        """落子，用于初始化棋局"""
        cell = self.cells[index]
        old_chess = cell.chess
        if old_chess is not None:
            raise Exception("chess exists can not modify. cell[%d], old chess:%s, new chess:%s" %
                            (index, old_chess, chess))
        if not cell.set_chess(chess):
            raise Exception("set_chess failed. cell[%d], chess:%s" % (index, chess))
        if self._head_cell == cell:
            self._head_cell = cell.next_sibling
            cell.next_sibling = None
        else:
            prev_sibling: Cell = next((p_c for p_c in self.cells if p_c.next_sibling == cell))
            prev_sibling.next_sibling = cell.next_sibling
            cell.next_sibling = None

    def guess(self):
        """启动求解"""
        return self._head_cell.guess()

    def print_answer(self):
        """打印解"""
        self._answer_count += 1
        if self._answer_count == 1:
            print("%d◆\t\n%s" % (self._answer_count, repr(self)))
        else:
            print("%d◆\t%s" % (self._answer_count, str(self)))

    def __str__(self):
        text = ''
        i = 0
        for cell in self._cells:
            if i == 0:
                pass
            elif self.width > 0 and i % self.width == 0:
                text += " "
            else:
                text += ","

            if cell.chess is None:
                text += "_"
            else:
                text += str(cell.chess.name) if cell.chess.name != 0 else "_"
            i += 1
        return text

    def __repr__(self):
        text = ''
        i = 0
        for cell in self._cells:
            if i == 0:
                pass
            elif self.width > 0 and i % self.width == 0:
                text += "\n"
            else:
                text += ","

            if cell.chess is None:
                text += "_"
            else:
                text += str(cell.chess.name) if cell.chess.name != 0 else "_"
            i += 1
        return text
